#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdarg.h>
#include "cJSON.h"
#define N 64
#define UDP_PORT 8888
#define TCP_PORT 8885
// 封装system命令执行函数  解压
int system_cmd(const char *format, ...)
{
    va_list arg;
    pid_t status;
    char cmd[256]; // 存储格式化后的命令字符串
    // 将可变参数格式化为命令字符串（如 "cp -rf ..."）
    va_start(arg, format);
    vsnprintf(cmd, sizeof(cmd), format, arg);
    va_end(arg);
    // 执行系统命令，并获取退出状态
    status = system(cmd);
    // 检查执行结果：
    // - status == -1：命令执行失败（如shell启动失败）
    // - !WIFEXITED(status)：进程未正常退出（如被信号终止）
    // - WEXITSTATUS(status) != 0：命令退出码非0（执行失败）
    if (-1 == status || !WIFEXITED(status) || 0 != WEXITSTATUS(status))
    {
        printf("[%s]: FAIL\n", cmd);  // 打印失败命令
        return -1;
    }
    else
    {
        printf("[%s]: SUCCESS\n", cmd);
        return 0;
    }
}

int main(int argc, char const *argv[])
{
    int broadfd;
    // 创建数据报套接字 socket文件描述符
    broadfd = socket(AF_INET, SOCK_DGRAM, 0); // UDP
    if (broadfd < 0)
    {
        perror("sock err");
        return -1;
    }
    printf(" UDP socket success!\n");
    // 绑定套接字(ip+port)
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(UDP_PORT); // 端口号
    addr.sin_addr.s_addr = INADDR_ANY;

    socklen_t addrlen = sizeof(addr);
    // 绑定信息 bind
    if (bind(broadfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf(" UDP bind success!\n");
    ssize_t len;
    char buf[N] = {0};
    struct sockaddr_in cliaddr;

    // 接收soft"coffee"搜索包 soft发送来的设备搜索协议--约定字符串 (UDP数据报)
    bzero(buf, N);                                                             // 将内存区域清零
    len = recvfrom(broadfd, buf, N, 0, (struct sockaddr *)&cliaddr, &addrlen); // 发送端的网络信息

    // 判断是否是本公司产品：收到的数据是否"Zhiqing"
    if (strcmp(buf, "Zhiqing") != 0)
    {
        printf("not my company\n");
        return -1;
    }
    else
    {
        printf("搜索协议匹配成功\n");
    }
    // 向客户端cliaddr 回复yes，告诉soft软件，我收到了搜索协议，并且回复地址
    sendto(broadfd, "yes", 4, 0, (struct sockaddr *)&cliaddr, addrlen); // 接收端的网络信息
    printf("回复客户端 yes\n");
    // 变身为TCP服务器，准备接收软件的升级文件
    int tcpfd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcpfd < 0)
    {
        perror("sock err");
        return -1;
    }
    printf(" TCP socket success!\n");

    // 绑定
    if (bind(tcpfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf(" TCP bind success!\n");
    // 监听套接字
    if (listen(tcpfd, 5) < 0)
    {
        perror("listen err");
        return -1;
    }

    // 接收客户端的连接
    int clifd;
    // 接收客户端连接的地址
    clifd = accept(tcpfd, NULL, NULL);
    if (clifd < 0)
    {
        perror("accept err");
        return -1;
    }
    printf("accept success!\n");
    printf("soft ip:%s port:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
    printf("new cononect coming\n");

    // 接收客户端发来的升级问询包
    char update_buf[4096] = {0}; // 保存升级询问包信息
    size_t recvbyte = recv(clifd, update_buf, sizeof(update_buf), 0);
    printf("recvbyte = %ld\n", recvbyte); // 接收到的大小
    if (recvbyte < 0)
    {
        perror("recv err\n");
        return -1;
    }

    // 解析客户端发来的　JSON格式升级问询包  反序列化
    printf("Received data: \n");
    printf("%s\n", update_buf);
    cJSON *request_root = cJSON_Parse(update_buf);
    int type = cJSON_GetObjectItem(request_root, "type")->valueint;
    // cJSON *item_type = cJSON_GetObjectItem(root, "type");
    printf("type= %d\n", type);
    cJSON *data = cJSON_GetObjectItem(request_root, "data");
    cJSON *ver = cJSON_GetObjectItem(data, "version"); // 接收到的升级包版本
    // printf("%s:%d\n", item->string, item->valueint);
    cJSON *file_name = cJSON_GetObjectItem(data, "file_name");
    // printf("%s:%s\n", item->string, item->valuestring);
    cJSON *file_len = cJSON_GetObjectItem(data, "file_len");
    cJSON *md5 = cJSON_GetObjectItem(data, "md5");
    int need_upgrade = 0;
    if (type == 1)
    {
        // 读取本地 node.json 文件
        FILE *fp = fopen("./node.json", "r");
        if (fp == NULL)
        {
            perror("fopen err");
            return -1;
        }
        char node_json[4096] = {0};
        size_t bytes_read = fread(node_json, 1, sizeof(node_json) - 1, fp);
        node_json[bytes_read] = '\0'; // 添加字符串终止符
        fclose(fp);

        // 解析本地　node.json 文件　反序列化
        cJSON *root = cJSON_Parse(node_json);
        if (root == NULL)
        {
            printf("本地JSON 解析失败: %s\n", cJSON_GetErrorPtr());
            return -1;
        }
        //  解析点表中的ver 反序列化
        cJSON *node_ver = cJSON_GetObjectItem(root, "version"); // 解析点表中的版本
        printf("当前版本: %s, 请求版本: %s\n", node_ver->valuestring, ver->valuestring);

        // 回复客户端是否需要升级  序列化
        cJSON *resp_root = cJSON_CreateObject();
        cJSON_AddItemToObject(resp_root, "type", cJSON_CreateNumber(1));
        cJSON *resp_data = cJSON_CreateObject();

        if (strcmp(node_ver->valuestring, ver->valuestring) == 0)
        {
            cJSON_AddItemToObject(resp_data, "update", cJSON_CreateString("false"));
            printf("不需要升级e\n"); // 不需要升级
        }
        else
        {
            cJSON_AddItemToObject(resp_data, "update", cJSON_CreateString("true"));
            printf(" 需要升级\n"); // 需要升级
            need_upgrade = 1;
            cJSON_AddItemToObject(resp_root, "data", resp_data);
            char *resp_json = cJSON_Print(resp_root); // 生成JSON字符串
            if (send(clifd, resp_json, strlen(resp_json), 0) < 0)
            {
                perror("send err");
                return -1;
            }
            printf("需要升级的请求已发送\n");
            free(resp_json);
            resp_json = NULL;
        }
        cJSON_Delete(root);
    }
    close(clifd); // 用于通信的新文件描述符
    close(tcpfd); // 用于连接的文件描述符
    close(broadfd);  //关闭UDP广播的文件描述符

    // 建立新的TCP长连接 接收客户端发送的升级包
    if (need_upgrade) // 需要升级
    {
        printf("Waiting for data connection...\n");
        int data_tcpfd = socket(AF_INET, SOCK_STREAM, 0);
        if (data_tcpfd < 0)
        {
            perror("sock err");
            return -1;
        }
        printf(" TCP2 socket success!\n");
        struct sockaddr_in data_addr;
        data_addr.sin_family = AF_INET;
        data_addr.sin_addr.s_addr = INADDR_ANY;
        data_addr.sin_port = htons(TCP_PORT); // 指定新端口号
        // 绑定
        if (bind(data_tcpfd, (struct sockaddr *)&data_addr, sizeof(data_addr)) < 0)
        {
            perror("bind err");
            return -1;
        }
        printf(" TCP2 bind success!\n");
        // 监听套接字
        if (listen(data_tcpfd, 5) < 0)
        {
            perror("listen err");
            return -1;
        }
        // 接收客户端的连接
        int data_clifd;
        // 接收客户端连接的地址
        data_clifd = accept(data_tcpfd, NULL, NULL);
        if (data_clifd < 0)
        {
            perror("accept err");
            return -1;
        }
        printf("用于传输升级包的新TCP连接已建立!\n");
        // 准备接收文件
        int file_fd = open(file_name->valuestring, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (file_fd < 0)
        {
            perror("open file");
            close(data_clifd);
            close(data_tcpfd);
            return -1;
        }
        // 接收文件数据
        char buffer[4096];
        ssize_t bytes_read;
        size_t total_received = 0;
        while (total_received < file_len->valueint)
        {
            bytes_read = recv(data_clifd, buffer, sizeof(buffer), 0);
            if (bytes_read <= 0)
            {
                perror("recv file data");
                break;
            }
            write(file_fd, buffer, bytes_read);
            total_received += bytes_read;
            printf("\rReceiving: %zu/%d bytes (%.1f%%)",
                   total_received, file_len->valueint,
                   (float)total_received / file_len->valueint * 100);
            fflush(stdout);
        }
        printf("\nReceived %zu bytes\n", total_received);
        close(file_fd);
        // 验证文件大小
        int size_check = (total_received == file_len->valueint);
        printf("文件大小校验: %s\n", size_check ? "PASS" : "FAIL");

        // 计算MD5校验和
        FILE *md5_pipe;
        char md5_cmd[256];
        char calculated_md5[33] = {0}; // 　计算MD5码
        // 计算接收文件的MD5
        snprintf(md5_cmd, sizeof(md5_cmd), "md5sum %s | cut -d' ' -f1", file_name->valuestring);
        md5_pipe = popen(md5_cmd, "r");
        if (md5_pipe)
        {
            fgets(calculated_md5, sizeof(calculated_md5), md5_pipe);
            // 移除可能的换行符
            calculated_md5[strcspn(calculated_md5, "\n")] = 0;
            pclose(md5_pipe);
        }
        // 验证MD5
        int md5_check = (strcmp(calculated_md5, md5->valuestring) == 0);
        printf("MD5 校验: %s\n", md5_check ? "PASS" : "FAIL");
        printf("升级询问包MD5码: %s\n", md5->valuestring);
        printf("接收到的MD5码: %s\n", calculated_md5);
        // 发送传输结果  type = 2时 序列化
        cJSON *result_root = cJSON_CreateObject();
        cJSON_AddItemToObject(result_root, "type", cJSON_CreateNumber(2));
        cJSON *result_data = cJSON_CreateObject();
        // 只有当文件大小和MD5都验证通过时才返回成功
        if (size_check && md5_check)
        {
            cJSON_AddItemToObject(result_data, "trans_success", cJSON_CreateString("true"));
            printf("固件传输成功!\n");
        }
        else
        {
            cJSON_AddItemToObject(result_data, "trans_success", cJSON_CreateString("false"));
            printf("固件传输失败!\n");
        }
        cJSON_AddItemToObject(result_root, "data", result_data);
        char *result_json = cJSON_Print(result_root);
        // 发送结果
        if (send(data_clifd, result_json, strlen(result_json), 0) < 0)
        {
            perror("send result");
            return -1;
        }
        // printf("固件传输结果已发送!\n");
        free(result_json);
        result_json = NULL;
        cJSON_Delete(result_root);

        // 解压升级包
        if (size_check && md5_check)
        {
            // 发送传输结果  type = 3时 序列化
            cJSON *update_root = cJSON_CreateObject();
            cJSON_AddItemToObject(update_root, "type", cJSON_CreateNumber(3));
            cJSON *update_data = cJSON_CreateObject();
            // // 创建解压目录
            // if (system_cmd("mkdir -p ./update_temp") != 0) //命令执行成功返回0
            // {
            //     printf("创建解压目录失败！\n");
            // }
            // else
            // {
            //     printf("创建解压目录成功！\n");
            // }
            // 解压升级包到临时目录
            if (system_cmd("unzip -o %s -d ./update_temp", file_name->valuestring) != 0)
            {
                printf("解压升级包失败\n");
            }
            else
            {
                printf("解压升级包成功\n");
            }
            // 直接复制文件
            if (system_cmd("cp -rf ./update_temp/* ./") != 0)
            {
                printf("复制升级文件失败！\n");
            }
            else
            {
                printf("复制升级文件成功！\n");
            }
            // 清理临时目录
            system_cmd("rm -rf ./update_temp");
            // 更新版本信息
            FILE *fp = fopen("./node.json", "r");
            if (fp != NULL)
            {
                char node_json[4096] = {0};
                size_t bytes_read = fread(node_json, 1, sizeof(node_json) - 1, fp);
                node_json[bytes_read] = '\0';
                fclose(fp);

                cJSON *root = cJSON_Parse(node_json);
                if (root != NULL)
                {
                    // 更新版本号
                    cJSON_ReplaceItemInObject(root, "version", cJSON_CreateString(ver->valuestring));

                    // 写回文件
                    char *new_json = cJSON_Print(root);
                    fp = fopen("./node.json", "w");
                    if (fp != NULL)
                    {
                        fputs(new_json, fp);
                        fclose(fp);
                        cJSON_AddItemToObject(update_data, "update_success", cJSON_CreateString("true"));
                        printf("更新版本号成功\n");
                        free(new_json);
                        cJSON_Delete(root);
                    }
                    else
                    {
                        cJSON_AddItemToObject(update_data, "update_success", cJSON_CreateString("false"));
                        printf("更新版本号失败\n");
                    }
                    cJSON_AddItemToObject(update_root, "data", update_data);
                    char *update_json = cJSON_Print(update_root);
                    // 发送结果
                    if (send(data_clifd, update_json, strlen(update_json), 0) < 0)
                    {
                        perror("send err");
                        return -1;
                    }
                    printf("升级成功结果已发送!\n");
                    free(update_json);
                    update_json = NULL;
                    cJSON_Delete(update_root);
                }
            }
        }
        cJSON_Delete(request_root); // 客户端第一次发送的升级询问包
        close(data_clifd);  //关闭用于通信的文件描述符
        close(data_tcpfd);  //关闭用于连接的文件描述符
    }
    return 0;
}
